/*
 * ComplexMath.cs
 * 
 * Copyright (c) 2003-2005, dnAnalytics. All rights reserved.
*/
using System;

namespace dnAnalytics.Nli {
	///<summary>Provides trigonometric, logarithmic, and other common mathematical functions for complex types</summary>
	public sealed class ComplexMath {
		private static readonly ComplexDouble d_i = new ComplexDouble(0,1);
		private static readonly ComplexDouble d_ni = new ComplexDouble(0,-1);
		private static readonly ComplexFloat f_i = new ComplexFloat(0,1);
		private static readonly ComplexFloat f_ni = new ComplexFloat(0,-1);

		private ComplexMath() {}

		///<summary>Return the absolute value of a complex type calculated as the euclidean norm</summary>
		public static double Absolute(ComplexDouble value) {
			return Math.Sqrt(value.Real*value.Real + value.Imaginary*value.Imaginary);
		}

		///<summary>Return the absolute value of a complex type calculated as the euclidean norm</summary>
		public static float Absolute(ComplexFloat value) {
			return (float) Math.Sqrt(value.Real*value.Real + value.Imaginary*value.Imaginary);
		}

		///<summary>Calculate the complex argument of a complex type.  Also commonly referred to as the phase.</summary>
		public static double Argument(ComplexDouble value) {
			return Math.Atan(value.Imaginary/value.Real);
		}

		///<summary>Calculate the complex argument of a complex type.  Also commonly refereed to as the phase.</summary>
		public static float Argument(ComplexFloat value) {
			return (float) Math.Atan(value.Imaginary/value.Real);
		}

		///<summary>Calculate the 2-argument of a complex type.</summary>
		public static double Argument2(ComplexDouble value) {
			return Math.Atan2(value.Imaginary, value.Real);
		}

		///<summary>Calculate the 2-argument of a complex type.</summary>
		public static float Argument2(ComplexFloat value) {
			return (float) Math.Atan2(value.Imaginary, value.Real);
		}

		///<summary>Return the complex conjugate of a complex type</summary>
		public static ComplexDouble Conjugate(ComplexDouble value) {
			return new ComplexDouble(value.Real, -value.Imaginary);
		}

		///<summary>Return the complex conjugate of a complex type</summary>
		public static ComplexFloat Conjugate(ComplexFloat value) {
			return new ComplexFloat(value.Real, -value.Imaginary);
		}

		///<summary>Return the complex cosine of a complex type</summary>
		public static ComplexDouble Cos(ComplexDouble value) {
			return new ComplexDouble(Math.Cos(value.Real)*Math.Cosh(value.Imaginary), -Math.Sin(value.Real)*Math.Sinh(value.Imaginary));
		}

		///<summary>Return the complex cosine of a complex type</summary>
		public static ComplexFloat Cos(ComplexFloat value) {
			return new ComplexFloat((float) (Math.Cos(value.Real)*Math.Cosh(value.Imaginary)), -(float) (Math.Sin(value.Real)*Math.Sinh(value.Imaginary)));
		}

		///<summary>Return the complex hyperbolic cosine of a complex type</summary>
		public static ComplexDouble Cosh(ComplexDouble value) {
			return new ComplexDouble(Math.Cos(value.Imaginary)*Math.Cosh(value.Real), Math.Sin(value.Imaginary)*Math.Sinh(value.Real));
		}

		///<summary>Return the complex hyperbolic cosine of a complex type</summary>
		public static ComplexFloat Cosh(ComplexFloat value) {
			return new ComplexFloat((float) (Math.Cos(value.Imaginary)*Math.Cosh(value.Real)), (float) (Math.Sin(value.Imaginary)*Math.Sinh(value.Real)));
		}

		///<summary>Return the complex exponential of a complex type</summary>
		public static ComplexDouble Exp(ComplexDouble value) {
			return new ComplexDouble(Math.Exp(value.Real)*Math.Cos(value.Imaginary), Math.Exp(value.Real)*Math.Sin(value.Imaginary));
		}

		///<summary>Return the complex exponential of a complex type</summary>
		public static ComplexFloat Exp(ComplexFloat value) {
			return new ComplexFloat((float) (Math.Exp(value.Real)*Math.Cos(value.Imaginary)), (float) (Math.Exp(value.Real)*Math.Sin(value.Imaginary)));
		}


		///<summary>Raise 'leftSide' to the power of 'rightSide'</summary>
		///<param name="leftSide"><c>ComplexDouble</c> value as base</param>
		///<param name="rightSide"><c>ComplexDouble</c> value as exponent</param>
		public static ComplexDouble Pow(ComplexDouble leftSide, ComplexDouble rightSide) {
			return Exp(rightSide*ComplexMath.Log(leftSide));
		}

		///<summary>Raise 'leftSide' to the power of 'rightSide'</summary>
		///<param name="leftSide"><c>ComplexFloat</c> value as base</param>
		///<param name="rightSide"><c>ComplexFloat</c> value as exponent</param>
		public static ComplexFloat Pow(ComplexFloat leftSide, ComplexFloat rightSide) {
			return Exp(rightSide*ComplexMath.Log(leftSide));
		}

		///<summary>Return the complex logarithm of a complex type</summary>
		public static ComplexDouble Log(ComplexDouble value) {
			double r = ComplexMath.Absolute(value);
			double i = Math.Atan2(value.Imaginary, value.Real);
			if (i > Math.PI) {
				i -= (2.0*Math.PI);
			}
			return new ComplexDouble(Math.Log(r), i);
		}

		///<summary>Return the complex logarithm of a complex type</summary>
		public static ComplexFloat Log(ComplexFloat value) {
			double r = ComplexMath.Absolute(value);
			double i = Math.Atan2(value.Imaginary, value.Real);
			if (i > Math.PI) {
				i -= (2.0*Math.PI);
			}
			return new ComplexFloat((float) Math.Log(r), (float) i);
		}

		///<summary>Given two complex types return the one with the maximum norm</summary>
		public static ComplexDouble Max(ComplexDouble v1, ComplexDouble v2) {
			if (Norm(v1) >= Norm(v2)) {
				return v1;
			} else {
				return v2;
			}
		}

		///<summary>Given two complex types return the one with the maximum norm</summary>
		public static ComplexFloat Max(ComplexFloat v1, ComplexFloat v2) {
			if (Norm(v1) >= Norm(v2)) {
				return v1;
			} else {
				return v2;
			}
		}

		///<summary>Return the euclidean norm of a complex type</summary>
		public static double Norm(ComplexDouble value) {
			return ComplexMath.Absolute(value);
		}

		///<summary>Return the euclidean norm of a complex type</summary>
		public static float Norm(ComplexFloat value) {
			return ComplexMath.Absolute(value);
		}

		///<summary>Return the polar representation of a complex type</summary>
		public static ComplexDouble Polar(ComplexDouble value) {
			return new ComplexDouble(ComplexMath.Absolute(value), Math.Atan2(value.Imaginary, value.Real));
		}

		///<summary>Return the polar representation of a complex type</summary>
		public static ComplexFloat Polar(ComplexFloat value) {
			return new ComplexFloat(ComplexMath.Absolute(value), (float) (Math.Atan2(value.Imaginary, value.Real)));
		}

		///<summary>Returns the sine of a complex type</summary>
		public static ComplexDouble Sin(ComplexDouble value) {
			return new ComplexDouble(Math.Sin(value.Real)*Math.Cosh(value.Imaginary), Math.Cos(value.Real)*Math.Sinh(value.Imaginary));
		}

		///<summary>Returns the sine of a complex type</summary>
		public static ComplexFloat Sin(ComplexFloat value) {
			return new ComplexFloat((float) (Math.Sin(value.Real)*Math.Cosh(value.Imaginary)), (float) (Math.Cos(value.Real)*Math.Sinh(value.Imaginary)));
		}

		///<summary>Returns the hyperbolic sine of a complex type</summary>
		public static ComplexDouble Sinh(ComplexDouble value) {
			return new ComplexDouble(Math.Sinh(value.Real)*Math.Cos(value.Imaginary), Math.Cosh(value.Real)*Math.Sin(value.Imaginary));
		}

		///<summary>Returns the hyperbolic sine of a complex type</summary>
		public static ComplexFloat Sinh(ComplexFloat value) {
			return new ComplexFloat((float) (Math.Sinh(value.Real)*Math.Cos(value.Imaginary)), (float) (Math.Cosh(value.Real)*Math.Sin(value.Imaginary)));
		}

		///<summary>Returns the square root of a complex type</summary>
		public static ComplexDouble Sqrt(ComplexDouble value) {
			double r = ComplexMath.Absolute(value);
			double rpart = Math.Sqrt(.5*(r + value.Real));
			double ipart = Math.Sqrt(.5*(r - value.Real));
			if (value.Imaginary < 0) {
				ipart = -ipart;
			}
			return new ComplexDouble(rpart, ipart);
		}

		///<summary>Returns the square root of a complex type</summary>
		public static ComplexFloat Sqrt(ComplexFloat value) {
			double r = ComplexMath.Absolute(value);
			double rpart = Math.Sqrt(.5*(r + value.Real));
			double ipart = Math.Sqrt(.5*(r - value.Real));
			if (value.Imaginary < 0) {
				ipart = -ipart;
			}
			return new ComplexFloat((float) rpart, (float) ipart);
		}

		///<summary>Returns the tangent of a complex type</summary>
		public static ComplexDouble Tan(ComplexDouble value) {
			return ComplexMath.Sin(value)/ComplexMath.Cos(value);
		}

		///<summary>Returns the tangent of a complex type</summary>
		public static ComplexFloat Tan(ComplexFloat value) {
			return ComplexMath.Sin(value)/ComplexMath.Cos(value);
		}

		///<summary>Returns the hyperbolic tangent of a complex type</summary>
		public static ComplexDouble Tanh(ComplexDouble value) {
			return ComplexMath.Sinh(value)/ComplexMath.Cosh(value);
		}

		///<summary>Returns the hyperbolic tangent of a complex type</summary>
		public static ComplexFloat Tanh(ComplexFloat value) {
			return ComplexMath.Sinh(value)/ComplexMath.Cosh(value);
		}

		/// <summary>Returns the inverse sine of a complex type.</summary>
		public static ComplexDouble Asin(ComplexDouble value) {
			return d_ni*ComplexMath.Log(d_i*value + ComplexMath.Sqrt(1 - value*value));
		}

		/// <summary>Returns the inverse sine of a complex type.</summary>
		public static ComplexFloat Asin(ComplexFloat value) {
			return f_ni*ComplexMath.Log(f_i*value + ComplexMath.Sqrt(1 - value*value));
		}

		/// <summary>Returns the inverse cosine of a complex type.</summary>
		public static ComplexDouble Acos(ComplexDouble value) {
			return d_ni*ComplexMath.Log(value + d_i*ComplexMath.Sqrt(1 - value*value));
		}

		/// <summary>Returns the inverse cosine of a complex type.</summary>
		public static ComplexFloat Acos(ComplexFloat value) {
			return f_ni*ComplexMath.Log(value + f_i*ComplexMath.Sqrt(1 - value*value));
		}

		/// <summary>Returns the inverse tangent of a complex type.</summary>
		public static ComplexDouble Atan(ComplexDouble value) {
			return (1/(d_i * 2)) * (ComplexMath.Log((1+d_i * value)) - ComplexMath.Log((1-d_i * value)));
		}

		/// <summary>Returns the inverse tangent of a complex type.</summary>
		public static ComplexFloat Atan(ComplexFloat value) {
			return (1/(f_i * 2)) * (ComplexMath.Log((1+f_i * value)) - ComplexMath.Log((1-f_i * value)));
		}

		///<summary>Returns the inverse hyperbolic sine of a complex type</summary>
		public static ComplexDouble Asinh(ComplexDouble value) {
			return -ComplexMath.Log(ComplexMath.Sqrt(1 + value*value) - value);
		}

		///<summary>Returns the inverse hyperbolic sine of a complex type</summary>
		public static ComplexFloat Asinh(ComplexFloat value) {
			return -ComplexMath.Log(ComplexMath.Sqrt(1 + value*value) - value);
		}

		///<summary>Returns the inverse hyperbolic cosine of a complex type</summary>
		public static ComplexDouble Acosh(ComplexDouble value) {
			return 2 * ComplexMath.Log((ComplexMath.Sqrt(((value + 1)/2)) + ComplexMath.Sqrt((value - 1)/2)));
		}

		///<summary>Returns the inverse hyperbolic cosine of a complex type</summary>
		public static ComplexFloat Acosh(ComplexFloat value) {
			return 2 * ComplexMath.Log((ComplexMath.Sqrt(((value + 1)/2)) + ComplexMath.Sqrt((value - 1)/2)));
		}

		///<summary>Returns the inverse hyperbolic tangent of a complex type</summary>
		public static ComplexDouble Atanh(ComplexDouble value) {
			return .5*ComplexMath.Log((1 + value)/(1 - value));
		}

		///<summary>Returns the inverse hyperbolic tangent of a complex type</summary>
		public static ComplexFloat Atanh(ComplexFloat value) {
			return .5f*ComplexMath.Log((1 + value)/(1 - value));
		}
	}
}